///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#ifndef __SceneQuery_H__
#define __SceneQuery_H__

#include "OgrePrerequisites.h"
#include "math/axisAlignedBox.h"
#include "math/sphere.h"
#include "math/ray.h"
#include "OgreRenderOperation.h"
#include "math/planeBoundedVolume.h"

namespace Ogre {

    // forward declaration
    class SceneQueryListener;
	
	
	
	class _OgreExport SceneQuery : public MemAlloc_SceneCtrl
    {
    public:
        
        enum WorldFragmentType {
            /// Return no world geometry hits at all
            WFT_NONE,
            /// Return pointers to convex plane-bounded regions
            WFT_PLANE_BOUNDED_REGION,
            /// Return a single intersection point (typically RaySceneQuery only)
            WFT_SINGLE_INTERSECTION,
            /// Custom geometry as defined by the SceneManager
            WFT_CUSTOM_GEOMETRY,
            /// General RenderOperation structure
            WFT_RENDER_OPERATION
        };

        
        struct WorldFragment {
            /// The type of this world fragment
            WorldFragmentType fragmentType;
            /// Single intersection point, only applicable for WFT_SINGLE_INTERSECTION
            Vector3 singleIntersection;
            /// Planes bounding a convex region, only applicable for WFT_PLANE_BOUNDED_REGION
            list<Plane>::type* planes;
            /// Custom geometry block, only applicable for WFT_CUSTOM_GEOMETRY
            void* geometry;
            /// General render operation structure, fallback if nothing else is available
            RenderOperation* renderOp;
            
        };
    protected:
        SceneManager* mParentSceneMgr;
        uint32 mQueryMask;
		uint32 mQueryTypeMask;
        set<WorldFragmentType>::type mSupportedWorldFragments;
        WorldFragmentType mWorldFragmentType;
    
    public:
        
        SceneQuery(SceneManager* mgr);
        virtual ~SceneQuery();

        
        virtual void setQueryMask(uint32 mask);
        
        virtual uint32 getQueryMask(void) const;

        
        virtual void setQueryTypeMask(uint32 mask);
        
        virtual uint32 getQueryTypeMask(void) const;

		
        virtual void setWorldFragmentType(enum WorldFragmentType wft);

        
        virtual WorldFragmentType getWorldFragmentType(void) const;

        
        virtual const set<WorldFragmentType>::type* getSupportedWorldFragmentTypes(void) const
            {return &mSupportedWorldFragments;}

        
    };

    
    class _OgreExport SceneQueryListener
    {
    public:
        virtual ~SceneQueryListener() { }
        
        virtual bool queryResult(MovableObject* object) = 0;
        
        virtual bool queryResult(SceneQuery::WorldFragment* fragment) = 0;

    };

    typedef list<MovableObject*>::type SceneQueryResultMovableList;
    typedef list<SceneQuery::WorldFragment*>::type SceneQueryResultWorldFragmentList;
    
	struct _OgreExport SceneQueryResult : public MemAlloc_SceneCtrl
    {
        /// List of movable objects in the query (entities, particle systems etc)
        SceneQueryResultMovableList movables;
        /// List of world fragments
		SceneQueryResultWorldFragmentList worldFragments;
    };

    
    class _OgreExport RegionSceneQuery
        : public SceneQuery, public SceneQueryListener
    {
    protected:
        SceneQueryResult* mLastResult;
    public:
        
        RegionSceneQuery(SceneManager* mgr);
        virtual ~RegionSceneQuery();
        
        virtual SceneQueryResult& execute(void);

        
        virtual void execute(SceneQueryListener* listener) = 0;
        
        
        virtual SceneQueryResult& getLastResults(void) const;
        
        virtual void clearResults(void);

        
        bool queryResult(MovableObject* first);
        
        bool queryResult(SceneQuery::WorldFragment* fragment);
    };

    
    class _OgreExport AxisAlignedBoxSceneQuery : public RegionSceneQuery
    {
    protected:
        AxisAlignedBox mAABB;
    public:
        AxisAlignedBoxSceneQuery(SceneManager* mgr);
        virtual ~AxisAlignedBoxSceneQuery();

        
        void setBox(const AxisAlignedBox& box);

        
        const AxisAlignedBox& getBox(void) const;

    };

    
    class _OgreExport SphereSceneQuery : public RegionSceneQuery
    {
    protected:
        Sphere mSphere;
    public:
        SphereSceneQuery(SceneManager* mgr);
        virtual ~SphereSceneQuery();
        
        void setSphere(const Sphere& sphere);

        
        const Sphere& getSphere() const;

    };

    
    class _OgreExport PlaneBoundedVolumeListSceneQuery : public RegionSceneQuery
    {
    protected:
        PlaneBoundedVolumeList mVolumes;
    public:
        PlaneBoundedVolumeListSceneQuery(SceneManager* mgr);
        virtual ~PlaneBoundedVolumeListSceneQuery();
        
        void setVolumes(const PlaneBoundedVolumeList& volumes);

        
        const PlaneBoundedVolumeList& getVolumes() const;

    };


    /*
    /// Specialises the SceneQuery class for querying within a pyramid. 
    class _OgreExport PyramidSceneQuery : public RegionSceneQuery
    {
    public:
        PyramidSceneQuery(SceneManager* mgr);
        virtual ~PyramidSceneQuery();
    };
    */

    
    class _OgreExport RaySceneQueryListener 
    {
    public:
        virtual ~RaySceneQueryListener() { }
        
        virtual bool queryResult(MovableObject* obj, Real distance) = 0;

        
        virtual bool queryResult(SceneQuery::WorldFragment* fragment, Real distance) = 0;

    };
      
    
    struct _OgreExport RaySceneQueryResultEntry
    {
        /// Distance along the ray
        Real distance;
        /// The movable, or NULL if this is not a movable result
        MovableObject* movable;
        /// The world fragment, or NULL if this is not a fragment result
        SceneQuery::WorldFragment* worldFragment;
        /// Comparison operator for sorting
        bool operator < (const RaySceneQueryResultEntry& rhs) const
        {
            return this->distance < rhs.distance;
        }

    };
    typedef vector<RaySceneQueryResultEntry>::type RaySceneQueryResult;

    
    class _OgreExport RaySceneQuery : public SceneQuery, public RaySceneQueryListener
    {
    protected:
        Ray mRay;
        bool mSortByDistance;
        ushort mMaxResults;
        RaySceneQueryResult mResult;

    public:
        RaySceneQuery(SceneManager* mgr);
        virtual ~RaySceneQuery();
        
        virtual void setRay(const Ray& ray);
        
        virtual const Ray& getRay(void) const;
        
        virtual void setSortByDistance(bool sort, ushort maxresults = 0);
        
        virtual bool getSortByDistance(void) const;
        
        virtual ushort getMaxResults(void) const;
        
        virtual RaySceneQueryResult& execute(void);

        
        virtual void execute(RaySceneQueryListener* listener) = 0;

        
        virtual RaySceneQueryResult& getLastResults(void);
        
        virtual void clearResults(void);

        
        bool queryResult(MovableObject* obj, Real distance);
        
        bool queryResult(SceneQuery::WorldFragment* fragment, Real distance);




    };

    
    class _OgreExport IntersectionSceneQueryListener 
    {
    public:
        virtual ~IntersectionSceneQueryListener() { }
        
        virtual bool queryResult(MovableObject* first, MovableObject* second) = 0;

        
        virtual bool queryResult(MovableObject* movable, SceneQuery::WorldFragment* fragment) = 0;

        /* NB there are no results for world fragments intersecting other world fragments;
           it is assumed that world geometry is either static or at least that self-intersections
           are irrelevant or dealt with elsewhere (such as the custom scene manager) */
        
    
    };
        
    typedef std::pair<MovableObject*, MovableObject*> SceneQueryMovableObjectPair;
    typedef std::pair<MovableObject*, SceneQuery::WorldFragment*> SceneQueryMovableObjectWorldFragmentPair;
    typedef list<SceneQueryMovableObjectPair>::type SceneQueryMovableIntersectionList;
    typedef list<SceneQueryMovableObjectWorldFragmentPair>::type SceneQueryMovableWorldFragmentIntersectionList;
    
	struct _OgreExport IntersectionSceneQueryResult : public MemAlloc_SceneCtrl
    {
        /// List of movable / movable intersections (entities, particle systems etc)
        SceneQueryMovableIntersectionList movables2movables;
        /// List of movable / world intersections
        SceneQueryMovableWorldFragmentIntersectionList movables2world;
        
        

    };

    
    class _OgreExport IntersectionSceneQuery
        : public SceneQuery, public IntersectionSceneQueryListener 
    {
    protected:
        IntersectionSceneQueryResult* mLastResult;
    public:
        IntersectionSceneQuery(SceneManager* mgr);
        virtual ~IntersectionSceneQuery();

        
        virtual IntersectionSceneQueryResult& execute(void);

        
        virtual void execute(IntersectionSceneQueryListener* listener) = 0;

        
        virtual IntersectionSceneQueryResult& getLastResults(void) const;
        
        virtual void clearResults(void);

        
        bool queryResult(MovableObject* first, MovableObject* second);
        
        bool queryResult(MovableObject* movable, SceneQuery::WorldFragment* fragment);
    };
    
	
	

}
    


#endif
